// es5 构造函数 模拟类 函数的名字大写

function AnimalFn() {
  this.type = '哺乳类';
  this.a = {};
}

AnimalFn.prototype.home = {}
AnimalFn.prototype.fn = function () { }
// 类一类事物  具体的一个实例
// 私有属性 是外面拿不到的  
// 1.实例上的属性
// 2.公共属性
// 3.静态方法 静态属性 只能通过类来调用
let animal1 = new AnimalFn(); // new 实现原理
let animal2 = new AnimalFn(); // new 实现原理
console.log(animal1);

// 每个对象上都会有一个__proto__ 找所属类的原型 .__proto__ = X.prototype
console.log(animal1.__proto__ == AnimalFn.prototype);         //true
console.log(AnimalFn.prototype.__proto__ == Object.prototype);//true
console.log(Object.prototype.__proto__);                      //null

console.log(Object.__proto__ == Function.prototype);          //true
console.log(Function.__proto__ == Function.prototype);        //true
console.log(Object.__proto__ === Function.__proto__);         //true

// constructor
console.log(AnimalFn.prototype.constructor === AnimalFn);//true
console.log(animal1.constructor); // 获取的是类 无法拿到类实例的属性 可以拿到静态属性或者方法


// 继承
// 实例的属性 公共属性
//一般使用：call + Object.create 或 call + setPrototypeOf

function Animal(name) {
  this.type = '哺乳类'
  this.name = name;
}
Animal.prototype.eat = function () {
  console.log('吃饭')
}
function Tiger(name) {
  // Animal.call(name);
  Animal.call(this);//第1步
}
// 1)老虎增加方法 可以增加到自己的身上 还可以找到Animal原型
//Tiger.prototype = Animal.prototype //错误的
Tiger.prototype.__proto__ = Animal.prototype;//第2步，等价下面的方法
Object.setPrototypeOf(Tiger.prototype, Animal.prototype); //es6
let tiger = new Tiger();
console.log(tiger.type);
console.log(tiger.eat());


// 2)Object.create()  es5
function create(parentPrototype) {
  let Fn = function () { }
  Fn.prototype = parentPrototype; // 把父类的原型 放到Fn上
  return new Fn();
}
// 让tiger的原型 执行 Fn函数的实例
// 找的时候 想找自己身上 找不到找对应的链 链放的是fn的实例，fn找不到 会找到父类的原型
Tiger.prototype = Object.create(Animal.prototype, { constructor: { value: Tiger } });
let tiger1 = new Tiger();
console.log(tiger1.constructor);

// 3) 用不到 不能给父类传递参数
Tiger.prototype = new Animal(); // {动物的实例属性 公共属性}
let tiger2 = new Tiger('xxx');
console.log(tiger2.name)

/*-----------------------------------------------------------------*/

//es6
class Animal1 {
  //静态属性，类上的，只能被类访问
  //static flag = 'abc';es7语法，node不支持
  static flag() {
    return 123
  }
  //实例
  constructor(name) {
    this.name = name;
    this.eat = '吃肉';
    // 可以有方法
    function fn1(){}
  }
  //公有，原型上的，通过原型链访问
  gougou() {
    console.log(this);//Animal1
    return 'dididada'
  }
  jiji(temp) {
    if (!isNaN(temp)) {
      return Number(temp) + 20
    } else {
      return 'Hello |' + this.eat + '| ' + temp;
    }
  }
}
let p = new Animal1;
console.log(Animal1.flag())
console.log(p)
console.log(p.jiji(50))
console.log(p.jiji('bobo'))
console.log(p.gougou())


//继承

class Tiger5 extends Animal1 {

}
let tiger3 = new Tiger5()
console.log(tiger3.eat)
console.log(tiger3.gougou)
let tiger4 = new Tiger5('大老虎')
console.log(tiger4.name)
console.log(Tiger5.flag(), '静态方法可以被继承，只能通过类来调用')

class Tiger6 extends Animal1 {
  constructor(name) {
    super(name)
  }
}
let tiger7 = new Tiger6('小老虎')
console.log(tiger7.name)


class Ary extends Array {
  constructor() {
    super();//必须要加才能用this
    this.a = '10';
    this.b = '20'
  }
  fn(arg) {
    if (this.length > 4) {
      //使用super调用继承的数组方法排序，只要length>4就不会再添加项而是去排序
      super.sort(function (a, b) {
        return a - b
      })
    } else {
      if (arg < 1) {
        return this.push(this.a)
      } else if (arg == 1) {
        return this.push(1)
      } else {
        return this.push(this.b)
      }
    }
  }
}
let aa = new Ary;

class SuperArray extends Array {
  shuffle() {
    for (let i = this.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [this[i], this[j]] = [this[j], this[i]];//数组解构赋值，先拿到所有的值，再去找对应的项赋值
    }
  }
}
let a = new SuperArray(1, 2, 3, 4, 5);
console.log(a)
a.shuffle();
console.log(a)

//装饰器报错需要更改vscode设置：
//搜索experimentalDecorators，勾选"js/ts.implicitProjectConfig.experimentalDecorators": true
//浏览器不支持，需要babel转译es5才能运行
//装饰器 可以修饰类，类的属性，类的原型上的方法
//修饰的时候把这个类 属性... 传递给修饰的函数
//@flag             //装饰器不能加;号
//@flag1('哺乳类')   //可以传参
class Animal3 {
  @readonly
  PI = 3.14;
  name = 'xxx';
  @before
  say(a,b,c) {
    console.log('说话',a,b,c)
  }
}
//1.类的静态属性
// function flag(constructor) {
//   constructor.type = '哺乳类'
// }
// function flag(value){
//   return function(constructor) {
//   constructor.type = value;
// }
// console.log(Animal.type)
//2.类的属性（实例上的属性）
//target==Animal.prototype
function readonly(target,property,descriptor) {
  descriptor.writable = false;//设置成不可更改
}
let animal3 = new Animal3();
//animal3.PI = 3.15 //报错，装饰器设置了不可更改
function before(target,property,descriptor){
  let oldSay = descriptor.value
  descriptor.value = function(){
    console.log('before');
    oldSay.call(target,...arguments);
  }
}
animal3.say(1,2,3);

